package com.cskaoyan.order.service.impl;

import com.cskaoyan.mall.commons.constant.SysRetCodeConstants;
import com.cskaoyan.mall.commons.exception.BizException;
import com.cskaoyan.mall.commons.exception.ExceptionProcessorUtils;
import com.cskaoyan.mall.order.constant.OrderRetCode;
import com.cskaoyan.order.biz.TransOutboundInvoker;
import com.cskaoyan.order.biz.context.AbsTransHandlerContext;
import com.cskaoyan.order.biz.factory.OrderProcessPipelineFactory;
import com.cskaoyan.order.biz.handler.ItemHandler;
import com.cskaoyan.order.conf.OrderProperties;
import com.cskaoyan.order.constant.OrderConstants;
import com.cskaoyan.order.constant.OrderItemConstant;
import com.cskaoyan.order.converter.OrderConverter;
import com.cskaoyan.order.dal.entitys.Order;
import com.cskaoyan.order.dal.entitys.OrderItem;
import com.cskaoyan.order.dal.entitys.Stock;
import com.cskaoyan.order.dal.persistence.OrderItemMapper;
import com.cskaoyan.order.dal.persistence.OrderMapper;
import com.cskaoyan.order.dal.persistence.OrderShippingMapper;
import com.cskaoyan.order.dal.persistence.StockMapper;
import com.cskaoyan.order.dto.*;
import com.cskaoyan.order.service.OrderCoreService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import tk.mybatis.mapper.entity.Example;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
@Data
public class OrderCoreServiceImpl implements OrderCoreService {

    @Autowired
    OrderProperties orderProperties;

    @Autowired
    OrderMapper orderMapper;

    @Autowired
    OrderItemMapper orderItemMapper;

    @Autowired
    OrderShippingMapper orderShippingMapper;

    @Autowired
    OrderProcessPipelineFactory orderProcessPipelineFactory;

    @Autowired
    StockMapper stockMapper;

    @Autowired
    OrderConverter orderConverter;


    /**
     * 创建订单的处理流程
     *
     * @param request
     * @return
     */
    @Override
    @Transactional
    public CreateOrderResponse createOrder(CreateOrderRequest request) {
        CreateOrderResponse response = new CreateOrderResponse();
        try {
            //创建pipeline对象
            TransOutboundInvoker invoker = orderProcessPipelineFactory.build(request);

            //启动pipeline
            invoker.start();

            //获取处理结果
            AbsTransHandlerContext context = invoker.getContext();

            //把处理结果转换为response
            response = (CreateOrderResponse) context.getConvert().convertCtx2Respond(context);

        } catch (Exception e) {
            log.error("OrderCoreServiceImpl.createOrder Occur Exception :" + e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }

    /**
     * 取消订单，执行以下业务操作
     * 1. 修改订单状态为 已取消
     * 2. 将库存中被冻结的对应商品数释放
     * 3. 修改订单商品中对应商品的状态为 已释放
     *
     * @param request 请求对象
     * @return com.cskaoyan.order.dto.CancelOrderResponse
     * @author Chen_Feng
     * @since 2022/07/09 21:14
     */
    @Override
    @Transactional
    public CancelOrderResponse cancelOrder(CancelOrderRequest request) {

        CancelOrderResponse response = new CancelOrderResponse();

        try {
            // 获得订单id
            String orderId = request.getOrderId();

            // 修改订单状态, 将从初始化状态修改为取消状态，并更新操作时间
            doUpdateOrderStatus(orderId, OrderConstants.ORDER_STATUS_INIT, OrderConstants.ORDER_STATUS_TRANSACTION_CANCEL);

            // 释放被冻结的商品库存,同时修改订单商品表中商品状态
            freeLockedStock(orderId, this::freeOrderItemStatus);

            // 封装数据
            response.setCode(SysRetCodeConstants.SUCCESS.getCode());
            response.setMsg(SysRetCodeConstants.SUCCESS.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }

    /**
     * 删除订单，根据是否开启伪删除决定是删除数据还是隐藏数据
     * 当订单处于未付款时，删除订单前先取消订单
     * 当订单处于已支付但还未收货时，不允许执行删除订单
     *
     * @param request 请求对象
     * @return com.cskaoyan.order.dto.DeleteOrderResponse
     * @author Chen_Feng
     * @since 2022/07/10 0:00
     */
    @Override
    @Transactional
    public DeleteOrderResponse deleteOrder(DeleteOrderRequest request) {

        DeleteOrderResponse response = new DeleteOrderResponse();

        try {
            // 获得订单id，并查询订单信息
            String orderId = request.getOrderId();
            Order order = orderMapper.selectByOrderId(orderId, orderProperties.isDeletedFieldExist());

            if (order == null) {
                response.setCode(OrderRetCode.DB_EXCEPTION.getCode());
                response.setMsg("无该订单信息");
                return response;
            }

            // 按照删除的策略执行删除操作
            doDeleteOrder(order);

            // 封装数据进行返回
            response.setCode(SysRetCodeConstants.SUCCESS.getCode());
            response.setMsg(SysRetCodeConstants.SUCCESS.getMessage());
            return response;
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }

    /**
     * 根据订单id查询到对应商品并释放库存中被冻结的库存数
     *
     * @param orderId 订单的id
     * @return void
     * @author Chen_Feng
     * @since 2022/07/09 21:13
     */
    private void freeLockedStock(String orderId, ItemHandler itemHandler) {

        // 根据订单id查询订单商品中的对应商品的信息
        Example orderItemExample = new Example(OrderItem.class);
        orderItemExample.createCriteria()
                .andEqualTo("orderId", orderId)
                .andEqualTo("status", OrderItemConstant.LOCK_STOCK_STATUS);
        List<OrderItem> orderItemList = orderItemMapper.selectByExample(orderItemExample);

        // 修改库存商品的可用库存和冻结库存数
        for (OrderItem orderItem : orderItemList) {

            // 查询该商品对应的库存信息
            Stock stock = stockMapper.selectByPrimaryKey(orderItem.getItemId());

            Integer num = orderItem.getNum(); // 被锁定的商品库存
            Long beforeStockCount = stock.getStockCount(); // 释放之前的可用库存
            Integer beforeLockCount = stock.getLockCount(); // 释放之前的锁定库存

            // 如果锁定库存数小于订单商品中的购买数，则抛出异常
            if (beforeLockCount < num) {
                throw new BizException(OrderRetCode.DB_EXCEPTION.getCode(), OrderRetCode.DB_EXCEPTION.getMessage());
            }

            Long afterStockCount = beforeStockCount + num; // 释放之后的可用库存
            Integer afterLockCount = beforeLockCount - num; // 释放之后的锁定库存

            // 更新库存表
            Stock updateStock = new Stock();
            updateStock.setItemId(stock.getItemId());
            updateStock.setLockCount(afterLockCount);
            updateStock.setStockCount(afterStockCount);
            stockMapper.updateByPrimaryKeySelective(updateStock);

            itemHandler.handle(orderItem);
        }
    }

    /**
     * 更新订单商品表的状态由 已锁定 到 已释放
     *
     * @param orderItem 订单商品对应的一条数据信息对象
     * @return void
     * @author Chen_Feng
     * @since 2022/07/10 13:22
     */
    private void freeOrderItemStatus(OrderItem orderItem) {
        // 更新订单商品表的状态由 已锁定 到 已释放
        orderItem.setStatus(OrderItemConstant.FREE_STOCK_STATUS);
        orderItem.setUpdate(new Timestamp(System.currentTimeMillis()));
        orderItemMapper.updateByPrimaryKeySelective(orderItem);
    }

    /**
     * 在数据库中删除该条订单商品数据
     *
     * @param orderItem 订单商品对应的一条数据信息对象
     * @return void
     * @author Chen_Feng
     * @since 2022/07/10 13:26
     */
    private void doDeleteOrderItem(OrderItem orderItem) {
        // 删除该条订单商品数据信息
        orderItemMapper.deleteByPrimaryKey(orderItem.getId());
    }

    /**
     * 根据订单id修改订单表中订单状态，需要指定修改后的状态
     *
     * @param orderId  要修改的订单id
     * @param fromCode 订单修改之前需满足的状态
     * @param destCode 订单修改为的状态
     * @return void
     * @author Chen_Feng
     * @since 2022/07/10 23:17
     */
    private void doUpdateOrderStatus(String orderId, Integer fromCode, int destCode) {
        // 修改订单状态
        Example orderExample = new Example(Order.class);
        Example.Criteria criteria = orderExample.createCriteria();
        criteria.andEqualTo("orderId", orderId);
        if (fromCode != null) {
            criteria.andEqualTo("status", fromCode);
        }
        Order order = new Order();
        order.setUpdateTime(new Date());
        order.setStatus(destCode);
        orderMapper.updateByExampleSelective(order, orderExample);
    }

    /**
     * 执行删除订单的操作，根据指定的删除策略，删除方式略有不同
     * 如果开启了伪删除，那么删除操作是将对应订单数据的deleted字段设置为true
     * 如果没有开启伪删除，那么是真正的删除数据，并且会删除订单对应的订单商品表、物流信息
     * <p>
     * 如果订单是未付款状态，删除订单时会将冻结的库存进行释放
     * 付款之后--确认收货之前，订单不可被删除，防止用户误操作而导致订单无法被操作
     *
     * @param order
     * @return void
     * @author Chen_Feng
     * @since 2022/07/10 23:28
     */
    private void doDeleteOrder(Order order) {

        // 获得订单id
        String orderId = order.getOrderId();

        if (OrderConstants.ORDER_STATUS_INIT == order.getStatus()) {
            // 如果该订单未付款
            if (orderProperties.isOPEN_FAKE_DELETE()) {
                // 如果开启了伪删除，那么取消该订单,设置为隐藏
                // order.setStatus(OrderConstants.ORDER_STATUS_TRANSACTION_CANCEL);
                // order.setUpdateTime(new Date());
                // order.setDeleted(true);
                orderMapper.fakeDeleteOrderByOrderId(orderId, OrderConstants.ORDER_STATUS_TRANSACTION_CANCEL);
                // orderMapper.updateByPrimaryKeySelective(order);
                // 释放被冻结的商品库存,同时修改订单商品表中商品状态
                freeLockedStock(orderId, this::freeOrderItemStatus);
            } else {
                // 真正意义上删除订单表的数据
                orderMapper.deleteByPrimaryKey(orderId);
                // 释放被冻结的商品库存,同时删除订单商品表中对应的数据
                freeLockedStock(orderId, this::doDeleteOrderItem);
                // 删除物流信息表中数据
                orderShippingMapper.deleteByPrimaryKey(orderId);
            }

        } else if (OrderConstants.ORDER_STATUS_PAYED == order.getStatus()) {
            // 如果订单已支付但是未收货，不允许删除
            throw new BizException(OrderRetCode.PIPELINE_RUN_EXCEPTION.getCode(), "已付款但未确认收货的订单不允许删除");

        } else {
            if (orderProperties.isOPEN_FAKE_DELETE()) {
                // 开启了伪删除，将订单进行隐藏
                // order.setUpdateTime(new Date());
                // order.setDeleted(true);
                // orderMapper.updateByPrimaryKeySelective(order);
                orderMapper.fakeDeleteOrderByOrderId(orderId, OrderConstants.ORDER_STATUS_TRANSACTION_CANCEL);
            } else {
                // 未开启伪删除，真正删除订单、对应的订单商品、对应的物流信息
                orderMapper.deleteByPrimaryKey(orderId);
                Example orderItemExample = new Example(OrderItem.class);
                orderItemExample.createCriteria().andEqualTo("orderId", orderId);
                orderItemMapper.deleteByExample(orderItemExample);
                orderShippingMapper.deleteByPrimaryKey(orderId);
            }
        }
    }
}
